
#include <QRectF>
#include <QPixmap>
#include <QWidget>
#include <QPointF>
#include <QTextOption>
#include <QTransform>
#include <QPainter>
#include <QTransform>
#include <QStylePainter>
#include <QStyleOptionFocusRect>

#include "canvasgl.h"

#define floatLine( x1, y1, x2, y2) { glBegin( GL_LINE_STRIP );\
  glVertex2f( x1, y1 );\
  glVertex2f( x2, y2 );\
  glEnd( ); }


canvasgl::canvasgl(QWidget *parent, int y_dir_)
  : QGLWidget(parent)
{

  setFormat(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer));

  text_opacity = TRANSPARENT_;

  y_dir = y_dir_;
  vdistortion = 1.0;
  margin_draw = 1.05;

  g2off = NULL;
  raster_area = NULL;
  world_area  = NULL;
  world_raw   = NULL;

  world_area = new QRectF (0.0, 0.0, (double)width(), (double)height());
  world_raw  = new QRectF (0.0, 0.0, (double)width(), (double)height());

  // setWorld (0.0, 10.0, 0.0, 10.0);

  setMouseTracking (true);
  setFocusPolicy(Qt::ClickFocus);

  //pbuffer = new QGLPixelBuffer(width(), height(), format(), this);
  //pbuffer->makeCurrent();

  shift_key = false;
  control_key = false;

}

canvasgl::~canvasgl()
{

}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::buildOffscreen()
{

  raster_area = new QSize;
  raster_area->setWidth (width());
  raster_area->setHeight (height());

}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::adjustWorld()
{
  double  vpr;               // canvas viewport aspect ratio
  double  xc, yc;            // center of world area
  double  world_area_x, world_area_w, world_area_y, world_area_h;

  world_area->getRect (&world_area_x, &world_area_y, &world_area_w, &world_area_h);

  vpr = (double)height() / (double)width();
  xc = world_area_x + (world_area_w * 0.5);
  yc = world_area_y + (world_area_h * 0.5);
  if( (world_area->height()*vdistortion) > (vpr*world_area->width()) )
    world_area_w = (world_area_h*vdistortion) / vpr;
  else
    world_area_h = (world_area_w/vdistortion) * vpr;
  world_area_x = xc - (world_area_w * 0.5) * margin_draw;
  world_area_y = yc - (world_area_h * 0.5) * margin_draw;

  // adjust margin
  world_area_w *= margin_draw;
  world_area_h *= margin_draw;

  // set new world area
  world_area->setRect (world_area_x, world_area_y, world_area_w, world_area_h);

  glMatrixMode( GL_PROJECTION );
  glLoadIdentity( );
  gluOrtho2D( (GLdouble)world_area_x, (GLdouble)(world_area_x + world_area_w),
              (GLdouble)world_area_y, (GLdouble)(world_area_y + world_area_h) );
  glMatrixMode( GL_MODELVIEW );
  glLoadIdentity( );

  toler = (world_area_w + world_area_h) / 200.0;
}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::setWorld( double xmin, double xmax, double ymin, double ymax )
{
  if( world_area == NULL )
  {
    world_raw = new QRectF();
    world_area = new QRectF();
  }
  world_raw->setRect (xmin, ymin, xmax - xmin, ymax - ymin);
  world_area->setRect (xmin, ymin, xmax - xmin, ymax - ymin);

  adjustWorld();
}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::panWorldX( double fac )
{
  double translation;            // window horizontal translation
  double xmin, xmax, ymin, ymax; // new window limits
  double  world_area_x, world_area_w, world_area_y, world_area_h;

  world_area->getRect (&world_area_x, &world_area_y, &world_area_w, &world_area_h);

  translation = world_area_w * fac;
  xmin = world_area_x - translation;
  xmax = world_area_x + world_area_w - translation;
  ymin = world_area_y;
  ymax = world_area_y + world_area_h;
  setWorld( xmin, xmax, ymin, ymax );
}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::panWorldY( double fac )
{
  double translation;            // window vertical translation
  double xmin, xmax, ymin, ymax; // new window limits
  double  world_area_x, world_area_w, world_area_y, world_area_h;

  world_area->getRect (&world_area_x, &world_area_y, &world_area_w, &world_area_h);

  translation = world_area_h * fac;
  xmin = world_area_x;
  xmax = world_area_x + world_area_w;
  ymin = world_area_y - translation;
  ymax = world_area_y + world_area_h - translation;
  setWorld( xmin, xmax, ymin, ymax );
}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::scaleWorld( double fac )
{
  double scalefac = 1.0 + fac;   // scale factor from percentage factor
  double xc, yc;                 // center of world area
  double sizex, sizey;           // world area new half sizes
  double  world_area_x, world_area_w, world_area_y, world_area_h;

  world_area->getRect (&world_area_x, &world_area_y, &world_area_w, &world_area_h);

  xc = world_area_x + (world_area_w * 0.5);
  yc = world_area_y + (world_area_h * 0.5);
  sizex = (world_area_w*0.5) / scalefac;
  sizey = (world_area_h*0.5) / scalefac;
  setWorld( xc - sizex, xc + sizex, yc - sizey, yc + sizey );
}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::setVDistortion( double vdistortion )
{
  if( vdistortion > 0.0 ) this->vdistortion = vdistortion;
}

//
//////////////////////////////////////////////////////////////////////////
double canvasgl::getVDistortion()
{
  return( vdistortion );
}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::world2raster( double world_x, double world_y, QPoint *pt_raster )
{
  pt_world.setX (world_x);
  pt_world.setY (world_y);
  pt_world = identity.map( pt_world);

  pt_raster->setX ((int)pt_world.x());
  pt_raster->setY ((int)pt_world.y());
}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::raster2world( int raster_x, int raster_y, QPointF *pt_world_ )
{
#if 1

  GLdouble modelMatrix[16];
  GLdouble projeMatrix[16];
  GLint    viewport[4];
  double   zr = 1.0;
  double   xw, yw, zw;

  canvasToRaster(&raster_x, &raster_y);

  glGetDoublev( GL_MODELVIEW_MATRIX,  modelMatrix );
  glGetDoublev( GL_PROJECTION_MATRIX, projeMatrix );
  glGetIntegerv( GL_VIEWPORT, viewport );

  gluUnProject( (double)raster_x, (double)raster_y, zr, modelMatrix, projeMatrix,
    viewport, &xw, &yw, &zw );

  pt_world_->setX (xw);
  pt_world_->setY (yw);

#else
  pt_world.setX ((double)raster_x);
  pt_world.setY ((double)raster_y);

  if (identity.isInvertible())
  {
    QTransform inv_wc2rc = identity.inverted ();
    pt_world = inv_wc2rc.map( pt_world);
  }
  else
  {
    pt_world.setX (0.0);
    pt_world.setY (0.0);
  }

  pt_world_->setX (pt_world.x());
  pt_world_->setY (pt_world.y());
#endif
}


//
//////////////////////////////////////////////////////////////////////////
void canvasgl::refreshPixmap( )
{
  //pixmap = QPixmap(size());
  //pixmap.fill(this, 0, 0);

  ////// QPainter painter
  //QPainter painter(&pixmap);
  //painter.initFrom(this);
  // pbuffer->makeCurrent();


  //////////////////////////////////////////////////////////////////////////
  if( world_area == NULL )
  {
    world_area = new QRectF (0.0, 0.0, (double)width(), (double)height());
    world_raw  = new QRectF (0.0, 0.0, (double)width(), (double)height());
    adjustWorld();
  }
  else
  {
    world_area->setRect (world_raw->x(), world_raw->y(), 
      world_raw->width(), world_raw->height());
    adjustWorld();
  }

  // background
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);



  // redraw 
  redraw( NULL );

  // Draws the buffered image to the screen.
  update();
  //glFlush ();
}


//
//////////////////////////////////////////////////////////////////////////
void canvasgl::mousePressEvent( QMouseEvent *e)
{
  raster2world ( e->pos().x(), e->pos().y(), &ept_world);
  mousePressedWC ( e, &ept_world );
}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::mouseMoveEvent( QMouseEvent *e )
{
  raster2world ( e->pos().x(), e->pos().y(), &ept_world);
  mouseDraggedWC ( e, &ept_world );

}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::mouseReleaseEvent( QMouseEvent *e )
{
  raster2world ( e->pos().x(), e->pos().y(), &ept_world);
  mouseReleasedWC (e, &ept_world );
}


//
//////////////////////////////////////////////////////////////////////////
void canvasgl::wheelEvent (QWheelEvent *event)
{
  if (event->delta() > 0)
    scaleWorld(+0.2);
  if (event->delta() < 0)
    scaleWorld(-0.1);
}


//
//////////////////////////////////////////////////////////////////////////
void canvasgl::keyPressEvent( QKeyEvent * event )
{
  if (event->key() == Qt::Key_Shift)
    shift_key = true;
  if (event->key() == Qt::Key_Control)
    control_key = true;
}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::keyReleaseEvent( QKeyEvent * event )
{
  if (event->key() == Qt::Key_Shift)
    shift_key = false;
  if (event->key() == Qt::Key_Control)
    control_key = false;
}

//
//////////////////////////////////////////////////////////////////////////
QSize canvasgl::minimumSizeHint() const
{
  return QSize(6 * Margin, 4 * Margin);
}

//
//////////////////////////////////////////////////////////////////////////
QSize canvasgl::sizeHint() const
{
  return QSize(12 * Margin, 8 * Margin);
}


//
//////////////////////////////////////////////////////////////////////////
void canvasgl::fit()
{
  double xmin, xmax, ymin, ymax;
  this->findMaxMin(&xmin, &xmax, &ymin, &ymax);
  this->setWorld (xmin, xmax, ymin, ymax);
  refreshPixmap();
}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::initializeGL()
{
  qglClearColor(Qt::white);
  glShadeModel(GL_FLAT);

  glViewport( 0, 0, width(), height());
  setWorld (0.0, 10.0, 0.0, 10.0);

  glEnable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);
}
//
//////////////////////////////////////////////////////////////////////////
void canvasgl::resizeGL( int width, int height )
{
  glViewport(0, 0, width, height);
}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::paintGL()
{
  refreshPixmap();
}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::canvasToRaster( int *xr, int *yr )
{
 GLint    viewport[4];
 int      h;

/* Transform from IUP mouse raster position to GL viewport position. 
 * Origin of IUP canvas is at the upper left corner. Origin of GL 
 * viewport is at the lower left corner (it is assumed that the
 * GL viewport takes the entire canvas).
 */
 glGetIntegerv( GL_VIEWPORT, viewport );
 h = viewport[3];
 *yr = h - *yr;
}

//
//////////////////////////////////////////////////////////////////////////
void canvasgl::getWindow( double *xmin, double *xmax, double *ymin, double *ymax )
{
  double w, h;
  if (world_area != NULL)
  {
    world_area->getRect (xmin, ymin, &w, &h);
    *xmax = *xmin + w;
    *ymax = *ymin + h;
  }
  else
    *xmin = *xmax = *ymin = *ymax = 0;
}

